tmux parity: add interactive command coverage and align flag semantics with tmux#653
tmux parity: add interactive command coverage and align flag semantics with tmux#653
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #653 +/- ##
==========================================
+ Coverage 46.58% 46.98% +0.40%
==========================================
Files 22 23 +1
Lines 2372 3271 +899
Branches 390 699 +309
==========================================
+ Hits 1105 1537 +432
- Misses 1098 1376 +278
- Partials 169 358 +189 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Code reviewFound 4 issues:
Lines 700 to 714 in 857384a
Lines 822 to 829 in 857384a
Lines 1119 to 1158 in 857384a
Lines 1202 to 1206 in 857384a 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
bc12f79 to
64a9ad5
Compare
…matrix why: PR #653 builds failed on tmux 3.2a-3.5 (3.6/master cancelled as siblings). Investigation traced six categories: methods that need an attached client, tests/doctests missing version guards, and a tmux upstream regression in run-shell on 3.3a/3.4. what: - Server.show_messages: add target_client kwarg; cmd-show-messages.c uses format_create_from_target without -T/-J, so a TTY-less CI server raises 'no current client' unless -t <client> is supplied. - Server.server_access: add 3.3+ version guard (server-access was introduced in tmux 3.3 per CHANGES FROM 3.2a TO 3.3). - Server.run_shell doctest: drop stdout assertion. tmux 3.3a/3.4 do not pipe run-shell stdout back through cmdq; restored upstream in 3.5 by commit fb37d52d. - Server.{command_prompt,confirm_before,show_prompt_history, clear_prompt_history} doctests: gate interactive demos behind has_gte_version so the doctest is harmless on older tmux. - tests/test_server.py: skip test_server_access_list, test_show_prompt_history, test_clear_prompt_history on <3.3; skip test_run_shell_basic on <3.5; rewrite test_show_messages to spawn a control-mode client via the existing fixture and pass target_client. - tests/test_pane.py: skip test_split_percentage on <3.5 since split-window -p was broken in 3.4 (fixed per CHANGES 3.4 TO 3.5).
…matrix why: PR #653 builds failed on tmux 3.2a-3.5 (3.6/master cancelled as siblings). Investigation traced six categories: methods that need an attached client, tests/doctests missing version guards, and a tmux upstream regression in run-shell on 3.3a/3.4. what: - Server.show_messages: add target_client kwarg; cmd-show-messages.c uses format_create_from_target without -T/-J, so a TTY-less CI server raises 'no current client' unless -t <client> is supplied. - Server.server_access: add 3.3+ version guard (server-access was introduced in tmux 3.3 per CHANGES FROM 3.2a TO 3.3). - Server.run_shell doctest: drop stdout assertion. tmux 3.3a/3.4 do not pipe run-shell stdout back through cmdq; restored upstream in 3.5 by commit fb37d52d. - Server.{command_prompt,confirm_before,show_prompt_history, clear_prompt_history} doctests: gate interactive demos behind has_gte_version so the doctest is harmless on older tmux. - tests/test_server.py: skip test_server_access_list, test_show_prompt_history, test_clear_prompt_history on <3.3; skip test_run_shell_basic on <3.5; rewrite test_show_messages to spawn a control-mode client via the existing fixture and pass target_client. - tests/test_pane.py: skip test_split_percentage on <3.5 since split-window -p was broken in 3.4 (fixed per CHANGES 3.4 TO 3.5).
Code reviewFound 1 issue:
Lines 1066 to 1077 in 095758a Suggested fix: mirror the Other items considered and dropped below the 80 confidence threshold: ControlMode 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance against the 3 commits since the prior review (095758a..1704a54). 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
…matrix why: PR #653 builds failed on tmux 3.2a-3.5 (3.6/master cancelled as siblings). Investigation traced six categories: methods that need an attached client, tests/doctests missing version guards, and a tmux upstream regression in run-shell on 3.3a/3.4. what: - Server.show_messages: add target_client kwarg; cmd-show-messages.c uses format_create_from_target without -T/-J, so a TTY-less CI server raises 'no current client' unless -t <client> is supplied. - Server.server_access: add 3.3+ version guard (server-access was introduced in tmux 3.3 per CHANGES FROM 3.2a TO 3.3). - Server.run_shell doctest: drop stdout assertion. tmux 3.3a/3.4 do not pipe run-shell stdout back through cmdq; restored upstream in 3.5 by commit fb37d52d. - Server.{command_prompt,confirm_before,show_prompt_history, clear_prompt_history} doctests: gate interactive demos behind has_gte_version so the doctest is harmless on older tmux. - tests/test_server.py: skip test_server_access_list, test_show_prompt_history, test_clear_prompt_history on <3.3; skip test_run_shell_basic on <3.5; rewrite test_show_messages to spawn a control-mode client via the existing fixture and pass target_client. - tests/test_pane.py: skip test_split_percentage on <3.5 since split-window -p was broken in 3.4 (fixed per CHANGES 3.4 TO 3.5).
…matrix why: PR #653 builds failed on tmux 3.2a-3.5 (3.6/master cancelled as siblings). Investigation traced six categories: methods that need an attached client, tests/doctests missing version guards, and a tmux upstream regression in run-shell on 3.3a/3.4. what: - Server.show_messages: add target_client kwarg; cmd-show-messages.c uses format_create_from_target without -T/-J, so a TTY-less CI server raises 'no current client' unless -t <client> is supplied. - Server.server_access: add 3.3+ version guard (server-access was introduced in tmux 3.3 per CHANGES FROM 3.2a TO 3.3). - Server.run_shell doctest: drop stdout assertion. tmux 3.3a/3.4 do not pipe run-shell stdout back through cmdq; restored upstream in 3.5 by commit fb37d52d. - Server.{command_prompt,confirm_before,show_prompt_history, clear_prompt_history} doctests: gate interactive demos behind has_gte_version so the doctest is harmless on older tmux. - tests/test_server.py: skip test_server_access_list, test_show_prompt_history, test_clear_prompt_history on <3.3; skip test_run_shell_basic on <3.5; rewrite test_show_messages to spawn a control-mode client via the existing fixture and pass target_client. - tests/test_pane.py: skip test_split_percentage on <3.5 since split-window -p was broken in 3.4 (fixed per CHANGES 3.4 TO 3.5).
…matrix why: PR #653 builds failed on tmux 3.2a-3.5 (3.6/master cancelled as siblings). Investigation traced six categories: methods that need an attached client, tests/doctests missing version guards, and a tmux upstream regression in run-shell on 3.3a/3.4. what: - Server.show_messages: add target_client kwarg; cmd-show-messages.c uses format_create_from_target without -T/-J, so a TTY-less CI server raises 'no current client' unless -t <client> is supplied. - Server.server_access: add 3.3+ version guard (server-access was introduced in tmux 3.3 per CHANGES FROM 3.2a TO 3.3). - Server.run_shell doctest: drop stdout assertion. tmux 3.3a/3.4 do not pipe run-shell stdout back through cmdq; restored upstream in 3.5 by commit fb37d52d. - Server.{command_prompt,confirm_before,show_prompt_history, clear_prompt_history} doctests: gate interactive demos behind has_gte_version so the doctest is harmless on older tmux. - tests/test_server.py: skip test_server_access_list, test_show_prompt_history, test_clear_prompt_history on <3.3; skip test_run_shell_basic on <3.5; rewrite test_show_messages to spawn a control-mode client via the existing fixture and pass target_client. - tests/test_pane.py: skip test_split_percentage on <3.5 since split-window -p was broken in 3.4 (fixed per CHANGES 3.4 TO 3.5).
…ent, key-name flags
why: send-keys has many useful flags (reset terminal, hex input, repeat count,
format expansion, copy-mode commands) that were not exposed in the Python API.
what:
- Add reset (-R), copy_mode_cmd (-X), repeat (-N), expand_formats (-F),
hex_keys (-H), target_client (-c, 3.4+), key_name (-K, 3.4+) parameters
- Version-gate target_client and key_name with has_gte_version("3.4")
- Add SendKeysCase NamedTuple parametrized tests for all new flags
… flags why: select-pane has rich flag support for directional navigation, pane marking, and input control that was not exposed in the Python API. what: - Add direction (-D/-U/-L/-R), last (-l), keep_zoom (-Z), mark (-m), clear_mark (-M), disable_input (-d), enable_input (-e) parameters - Reuse existing ResizeAdjustmentDirection enum for direction flags - Skip deprecated -P (style) and -g (show style) flags - Add tests for direction, last pane, mark/clear, and input toggle
…, and style flags why: display-message supports many useful flags for format queries and output control that were not exposed in the Python API. what: - Add format_string (-F), all_formats (-a), verbose (-v), no_expand (-I), target_client (-c), delay (-d), notify (-N), list_formats (-l, 3.4+), no_style (-C, 3.6+) parameters - Version-gate list_formats and no_style with has_gte_version - Fix cmd argument handling: only pass when non-empty - Add DisplayMessageCase NamedTuple parametrized tests
why: select-layout supports flags for spreading panes evenly and cycling through layouts that were not exposed in the Python API. what: - Add spread (-E), next_layout (-n), previous_layout (-o) parameters - Validate mutual exclusion between layout string and flag parameters - Add tests for spread, next/previous cycling, and mutual exclusion
…number flags why: move-window supports flags for positioning, conflict resolution, and renumbering that were not exposed in the Python API. what: - Add after (-a), before (-b), no_select (-d), kill_target (-k), renumber (-r) parameters to move_window() - Add tests for kill_target, renumber, and no_select behaviors
why: new-session supports flags for detaching other clients, suppressing initial sizing, and specifying config files that were not exposed. what: - Add detach_others (-D), no_size (-X), config_file (-f) parameters - Skip -A (attach-or-create) as it requires a terminal and does not work in libtmux's programmatic non-terminal context - Add test for config_file parameter
why: new-window supports flags for replacing existing windows at a target index and selecting existing windows by name that were not exposed. what: - Add kill_existing (-k) and select_existing (-S) parameters to new_window() - -k destroys existing window at target index before creating new one - -S selects existing window with matching name instead of creating new - Add tests for both flags
why: split-window supports -p for percentage-based sizing which is more intuitive than the absolute cell count provided by the existing size parameter. what: - Add percentage (-p) parameter to Pane.split(), mutually exclusive with size - Validate that size and percentage are not both specified - Add tests for percentage split and mutual exclusion
…kup flags
why: capture-pane supports flags for alternate screen capture, silent error
handling, and markup escaping that were not exposed.
what:
- Add alternate_screen (-a), quiet (-q), escape_markup (-M, 3.6+) parameters
- Version-gate escape_markup with has_gte_version("3.6")
- Add tests for quiet, alternate_screen, and escape_markup flags
…en to set_environment why: show-options supports -q (quiet) and -v (values only) flags, and set-environment supports -F (format expansion) and -h (hidden) flags that were not exposed in the Python API. what: - Add quiet (-q) and values_only (-v) to _show_options_raw() - Add expand_format (-F) and hidden (-h) to set_environment() - Add tests for quiet show_options, hidden env vars, and format expansion
…story
why: clear-history is useful for clearing pane scrollback buffers,
especially important for test isolation and monitoring workflows.
what:
- Add clear_history() method with clear_pane (-H, 3.4+) parameter
- Version-gate -H flag with has_gte_version("3.4")
- Add test verifying history is cleared after sending commands
…window why: swap-pane and swap-window are core layout manipulation commands needed for programmatic pane and window reordering. what: - Add Pane.swap() with target, detach (-d), move_up (-U), move_down (-D), keep_zoom (-Z) parameters wrapping swap-pane - Add Window.swap() with target, detach (-d) parameters wrapping swap-window - Add tests verifying pane indices and window indices swap correctly
…_formats
why: -I opens pane stdin input mode (window_pane_start_input), not
suppress expansion. -l (tmux 3.4) is the correct literal flag.
list_formats mapped to -l with doc "List format variables" — but
-l suppresses expansion, not lists variables; -a (all_formats)
already covers listing, making list_formats a wrong duplicate.
what:
- Fix no_expand to emit -l with has_gte_version("3.4") guard + warn on older
- Remove list_formats param from both overloads, implementation, and docstring
- Update no_expand docstring: "-l flag. Requires tmux 3.4+"
- Remove list_formats test case; add no_expand test verifying literal output
…matrix why: PR #653 builds failed on tmux 3.2a-3.5 (3.6/master cancelled as siblings). Investigation traced six categories: methods that need an attached client, tests/doctests missing version guards, and a tmux upstream regression in run-shell on 3.3a/3.4. what: - Server.show_messages: add target_client kwarg; cmd-show-messages.c uses format_create_from_target without -T/-J, so a TTY-less CI server raises 'no current client' unless -t <client> is supplied. - Server.server_access: add 3.3+ version guard (server-access was introduced in tmux 3.3 per CHANGES FROM 3.2a TO 3.3). - Server.run_shell doctest: drop stdout assertion. tmux 3.3a/3.4 do not pipe run-shell stdout back through cmdq; restored upstream in 3.5. - Server.{command_prompt,confirm_before,show_prompt_history, clear_prompt_history} doctests: gate interactive demos behind has_gte_version so the doctest is harmless on older tmux. - tests/test_server.py: skip test_server_access_list, test_show_prompt_history, test_clear_prompt_history on <3.3; skip test_run_shell_basic on <3.5; rewrite test_show_messages to spawn a control-mode client via the existing fixture and pass target_client. - tests/test_pane.py: skip test_split_percentage on <3.5 since split-window -p was broken in 3.4 (fixed per CHANGES 3.4 TO 3.5).
…coverage why: codecov/project failed because the new wrappers' parameter branches were untested (patch coverage 40.97%). Server.display_menu had no test at all; Pane.display_popup had two ad-hoc tests covering only a handful of flags. what: - tests/test_pane.py: replace test_display_popup_runs_command and test_display_popup_with_dimensions with a parametrized test_display_popup_flags driven by DisplayPopupCase, exercising basic, dimensions, position, start_directory, and the 3.3+ flags (title, border_lines, style, border_style, environment). Add test_display_popup_close_on_success for the -EE branch in isolation, test_display_popup_mutual_exclusion for the ValueError guard, and test_display_popup_close_existing for the -C branch. - tests/test_server.py: add DisplayMenuCase + test_display_menu_flags covering basic, title, position, target_pane, starting_choice, and the 3.3+ flags (border_lines, style, border_style). Per Server.display_menu's own docstring the wrapper cannot run under ControlMode (tty.sy=0 makes menu_prepare return NULL and the call hangs); the test stubs server.cmd to capture and assert the constructed argv, the only deviation from the suite's "use real tmux" pattern. The deviation is documented in the test docstring per AGENTS.md guidance. Patch coverage on server.py 62%→68%, pane.py 64%→70% (line-only; branch coverage gains are larger since each parametrized case exercises a distinct `if param is not None:` branch).
why: tmux's `detach-client -a` is server-wide (cmd-detach-client.c:92-101 loops the global client list with no session filter). The wrapper at session.py:291 emitted `-a -t <client>` and was documented as session-scoped, so callers asking to "keep target attached, detach others" silently detached clients in other sessions too. what: - Session.detach_client(all_clients=True, target_client=...) now enumerates clients via `list-clients -t <session_id>` and issues one `detach-client -t <client>` per non-target client, instead of the broken `-a -t` form. - Docstring expanded to document the upstream `-a` semantics and why the wrapper takes the manual route. - Add test_detach_client_all_clients_session_scoped: opens a client in another session via control_mode and asserts it remains attached after the call.
why: tmux's display-message -C does not suppress style output. Per
the introducing change ("Add display-message -C flag to update pane
while message is displayed", first in tmux 3.6) the flag is passed
to status_message_set as the no_freeze parameter. It allows the
pane to keep updating while the message is shown.
The wrapper labelled `-C` as `no_style: bool` and documented it as
"Suppress style output", which is wrong. Callers asking for
"suppress styles" silently get the unrelated update-pane behaviour.
what:
- Rename Pane.display_message kwarg `no_style` → `update_pane` on
the function and both @Overloads.
- Rewrite the docstring to describe the actual behaviour and cite
the upstream "Add display-message -C flag" change.
- Update the deferred warnings.warn message to match the new name.
why: pane.py and server.py inlined `from libtmux.common import has_gte_version` and `import warnings` ~15 times across method bodies, with no circular-import reason. Each repetition is slop that obscures the module's dependencies and prevents ruff from grouping imports. AGENTS.md "code should not declare what it needs over and over" applies in spirit. what: - Hoist `import warnings` and `from libtmux.common import has_gte_version` to the top of pane.py. - Hoist `from libtmux.common import has_gte_version` to the top of server.py (warnings was not used there). - Drop the inline imports inside ~12 method bodies. - Update tests/test_pane_capture_pane.py:test_capture_pane_trim_trailing_warning to monkeypatch `libtmux.pane.has_gte_version` instead of `libtmux.common.has_gte_version` — `from X import Y` binds Y in the importer's namespace, so once hoisted the pane module's binding is what the wrapper resolves at call time.
…gv check why: the previous doctest only verified the bound method exists (`>>> server.display_menu # doctest: +ELLIPSIS` → `<bound method ...>`), which is the exact anti-pattern AGENTS.md forbids. End-to-end execution can't be doctested — once tmux prepares a menu it returns CMD_RETURN_WAIT and the cmdq blocks on user selection (cmd-display-menu.c:374), so the previous doc incorrectly cited menu_prepare returning NULL. what: - Rewrite the docstring example to monkeypatch `server.cmd`, invoke `display_menu` with a representative flag set, and assert on the captured argv. Same approach as test_display_menu_flags in tests/test_server.py. - Add `monkeypatch` to the doctest namespace via conftest.py so the example can use it. The fixture is already collected for every test; exposing it to doctests is one extra line. - Update the prose to cite the correct CMD_RETURN_WAIT cause.
why: tmux's display-menu accepts -H selected-style (3.4+), -M always-mouse (3.5+), and -O stay-open (3.2+). The wrapper omitted all three, leaving callers without a way to highlight the selected item, force mouse mode, or keep the menu open after a release. what: - Add selected_style: str | None (-H), mouse: bool | None (-M), stay_open: bool | None (-O) parameters to Server.display_menu. - selected_style and mouse are version-gated with warnings.warn on unsupported tmux; stay_open ships unconditionally (3.2+ is older than the project's minimum). - Hoist `import warnings` into server.py top-level imports. - Extend test_display_menu_flags with three new cases (with_stay_open, with_selected_style_v34, with_mouse_v35) and teach the assertion to skip bool values (which emit only the switch, not a value).
why: tmux's display-popup accepts three flags the wrapper omitted: - -B (3.3+) opens the popup with no border at all and overrides -b border-lines unconditionally (cmd-display-menu.c, lines = BOX_LINES_NONE) - -k (3.6+) dismisses the popup on any keypress after the inner command exits (cmd-display-menu.c, POPUP_CLOSEANYKEY) - -N (3.6+) clears all auto-close flags so the popup is not auto-dismissed (cmd-display-menu.c, flags = 0) what: - Add no_border (-B), close_on_any_key (-k), no_keys (-N) kwargs to Pane.display_popup with the right per-flag version guards. - Reject no_border=True + border_lines=... with ValueError, mirroring the existing close_on_exit/close_on_success guard. tmux's -B overrides -b regardless, so the combination is meaningless. - Extend test_display_popup_flags with no_border_v33, close_on_any_key_v36, no_keys_v36 cases. - Add test_display_popup_no_border_with_border_lines_rejects.
why: tmux's command-prompt accepts three flags the wrapper omitted: - -F (3.3+) passes the template through args_make_commands_prepare so format strings expand - -l (3.6+) disables splitting the prompt on commas — treat the whole prompt as a single literal - -e (post-3.6) closes the prompt when the user empties it via backspace (PROMPT_BSPACE_EXIT) what: - Add expand_format (-F), literal (-l), bspace_exit (-e) kwargs to Server.command_prompt with per-flag version guards. - Add test_command_prompt_extra_flags using the monkeypatch argv pattern from test_display_menu_flags. End-to-end behaviour for these flags depends on tmux internals (format expansion, comma splitting, backspace exit) that are awkward to drive headless; the argv check confirms the wrapper emits the right flag.
why: tmux's copy-mode accepts two flags the wrapper omitted: - -s source-pane (3.2+) lets a pane display another pane's history in copy mode — useful for scrolling/copying from one pane into an editor in another - -d (3.5+) page-down on entry if already in copy mode what: - Add page_down (-d, version-gated to 3.5+) and source_pane (-s, unconditional since 3.2 is older than the project minimum) to Pane.copy_mode. - Add test_copy_mode_source_pane (cross-pane history exercise) and test_copy_mode_page_down (3.5+ skipif).
why: tmux's choose-tree accepts a much larger surface than the wrapper exposed (cmd-choose-tree.c:36, args="F:f:GK:NO:rstwyZ"). Real users need at least format/filter/sort to drive the chooser programmatically, plus -Z to zoom while picking. what: - Add format_string (-F), filter_expression (-f), sort_order (-O), reverse (-r), zoom (-Z) kwargs to Pane.choose_tree. - Use filter_expression rather than filter to avoid shadowing the Python builtin (ruff A002). - Add test_choose_tree_with_flags exercising all five.
why: capture_pane hardcoded -p (pipe to caller's stdout) and offered no way to send the capture into a named tmux buffer for later cross- pane consumption — a real feature of cmd-capture-pane.c (args="ab:CeE:JMNpPqS:Tt:"). what: - Add to_buffer: str | None kwarg. When set, the wrapper omits -p, emits -b <buffer> and returns None instead of stdout. - Use @t.overload to keep the existing list[str] return type for the default path; only the to_buffer-set call returns None. - Add test_capture_pane_to_buffer that captures into a named buffer and verifies the marker survived via show-buffer / delete-buffer.
why: tmux's show-messages takes -T (list terminal capabilities) and -J (print job server summary) in addition to the message-log default (cmd-show-messages.c:41 args="JTt:"). The wrapper docstring already referenced both, but only -t was exposed. -T and -J are early-return paths in cmd-show-messages.c that don't go through format_create_from_target, so they work clientless — handy for debugging tmux from a headless test run. what: - Add terminals (-T) and jobs (-J) bool kwargs to Server.show_messages. - Update the docstring to note the clientless modes. - Add test_show_messages_terminals_jobs which exercises both modes without spinning up a control_mode client.
…rite why: tmux's server-access supports forcing a user to attach read-only (-r) or allowing read-write attach (-w) — both implicitly allow the user if not yet in the ACL (cmd-server-access.c:108-145). The wrapper omitted both, leaving callers without the controls that make this command useful. what: - Add read_only (-r) and write (-w) bool kwargs to Server.server_access. Mutually exclusive — tmux rejects -r -w with "cannot be used together", and the wrapper raises ValueError early to give a clearer Python-side trace. - Add test_server_access_read_only_write_mutex for the guard and test_server_access_argv (monkeypatch capture) for the emitted flags. server-access's actual side effect requires real OS users and ACL state, so end-to-end testing is out of scope.
why: test_detach_client and test_detach_client_no_target_detaches_all_session_clients covered the same path (no target_client → -s session_id scoping) with identical fixture shape; the latter is strictly stronger because it asserts on two attached clients rather than one. Keeping both adds maintenance cost without adding signal. what: - Remove test_detach_client. The remaining tests (no_target_*, target_client, all_clients_session_scoped) cover the three real branches of Session.detach_client.
why: BufferCase already had an `append: bool | None` field but no parametrisation case actually exercised it — the append behaviour lived in a duplicative test_buffer_append function. The remaining buffer tests (delete, save_load, save_append, list_buffers) test genuinely different operations than set+show variations and stay as their own test functions. what: - Add a `set_show_append` BufferCase that seeds the buffer with "first" and appends "_second" via append=True, asserting the concatenated content. - Update test_buffer_set_show to interpret the existing `append` field by seeding the buffer and passing append=True. - Remove the standalone test_buffer_append function.
why: test_capture_pane_quiet, test_capture_pane_alternate_screen, and test_capture_pane_mode_screen each had the same "call with one flag, assert isinstance(result, list)" shape. Three almost-identical 3-line functions are exactly the case parametrised tests are for. The existing CAPTURE_PANE_CASES harness is intentionally focused on output-content assertions (run a command, check pattern in output) — folding flag smoke-tests into it would muddle that purpose. A small dedicated parametrise is the right fit. what: - Replace the three smoke functions with one parametrised test_capture_pane_flag_smoke driving (kwargs, min_tmux_version) cases for quiet, alternate_screen, and mode_screen (3.6+).
why: a previous commit annotated new params on existing methods but left the entirely-new methods unmarked. After the parity branch, ~55 brand-new public wrappers ship without a "versionadded" hint, so users can't tell from the docs which API landed in 0.45 vs an older release. The Weave review's three reviewers all flagged this (consensus: Suggestion → Important). what: - Insert `.. versionadded:: 0.45` into the docstring of every new public method on Server, Session, Window, and Pane. - 27 methods on server.py, 5 on session.py, 7 on window.py, 16 on pane.py. - Existing methods (capture_pane, display_message, Pane.select, Window.select, …) are left alone — they predate 0.45.
why: test_show_messages_terminals_jobs assumed -T/-J short-circuit
before client lookup. That only holds on tmux >= 3.6, after the
upstream "Allow show-messages to work without a client" change
added CMD_CLIENT_CANFAIL to cmd_show_messages_entry. On
3.2a/3.3a/3.4/3.5 the command queue rejects the call with
"no current client" before cmd_show_messages_exec runs, so the
clientless codepath is unreachable.
what:
- pytest.skip on tmux < 3.6 via has_gte_version("3.6")
- replace docstring rationale with the actual upstream cause
why: parity tests deliberately exercise version-specific tmux behaviour, so a failure on one tmux-version row should not cancel sibling jobs. With fail-fast on, a single failing version makes gh pr checks show all six rows as red and hides which versions actually pass. what: - set strategy.fail-fast: false on the build matrix
why: test_command_prompt_extra_flags[bspace_exit] failed on tmux 3.2a because Server.command_prompt unconditionally passes -b, which requires tmux 3.3+ — the wrapper raises before the monkeypatched cmd is ever called. The bspace_exit parametrise tuple had min_tmux_version=None, treating "version needed for this flag" rather than "version needed for the wrapper". what: - set min_tmux_version="3.3" for the bspace_exit case - comment why the gate is the wrapper-minimum, not the flag-minimum
why: command_prompt(bspace_exit=True) emitted -e unconditionally, but
the flag was added upstream after tmux 3.6 and is not in any
tagged release (verified: tag --contains returns empty; tmux 3.6a
errors with "unknown flag -e"). The wrapper would break on every
released version. Sibling literal flag (3.6+) shows the correct
guard pattern.
what:
- bump TMUX_MAX_VERSION from "3.6" to "3.7" so master compares as
"3.7-master" >= "3.7", letting bspace_exit work on master while
still failing the gate on tagged releases 3.2a-3.6a
- guard the -e append with has_gte_version("3.7"); warn-and-ignore
on older versions, matching the literal pattern at the same site
- update test_command_prompt_extra_flags[bspace_exit] gate to "3.7"
so the case skips on every currently-released tmux
- bump test_version_parsing[next_version] fixture from "next-3.7" to
"next-3.8" since "3.7" is no longer strictly greater than the new
TMUX_MAX_VERSION
why: os.pipe() allocates both ends in the parent before subprocess spawn. The existing try/finally only closed read_fd; if Popen raised (ENOMEM, exec error, missing binary), self._write_fd stayed open. __exit__ won't run because __enter__ never returned, so the registration cleanup at the bottom of __enter__ is also unreachable. Net: one leaked fd per failed Popen. what: - wrap the existing try/finally (which closes read_fd) in an outer try/except that closes self._write_fd if anything propagates out of Popen, then re-raises - use BaseException so KeyboardInterrupt during spawn also cleans up
why: test_allows_next_version asserts has_gt_version(TMUX_MAX_VERSION) for a mocked "tmux next-3.7" parse. A prior commit bumped TMUX_MAX_VERSION to "3.7", so parsed "3.7" is no longer strictly greater than the max — assertion fails on every tmux job. Mirror the same fixture bump already applied to tests/test_common.py. what: - TMUX_NEXT_VERSION "3.7" -> "3.8" so the parsed version stays one minor ahead of TMUX_MAX_VERSION
…SION 3.7 why: Record 0.56.x user-facing additions from the parity branch. what: - What's new: interactive command wrappers, buffer I/O suite, key bindings & shell execution, window/pane manipulation parity, filled-in flag coverage on existing methods, control_mode pytest fixture - Bug fixes: Window.move_window() refresh after move - Development: TMUX_MAX_VERSION bumped to 3.7
why: the 0.56.x section listed dozens of new methods, classes, and
the control_mode fixture as plain backticked text. Converting to
MyST roles with the ~-prefix shortlink (matching the 0.55.0
precedent) turns the changelog into a navigable index — every
method name renders as a clickable link to its autodoc entry.
what:
- CHANGES: convert method/class/data references in the 0.56.x
block to {meth}/{class}/{data}/{mod}/{fixture} roles, using the
~libtmux.X.Y form so rendered text shows just the short name
(matching 0.55.0 style).
- CHANGES (correctness): Window.wait_for is actually
Server.wait_for (lives in src/libtmux/server.py:489);
Session.rotate doesn't exist (dropped); send_prefix is on Pane,
not Server.
- docs/api/testing/pytest-plugin/fixtures.md: add an explicit
autofixture entry for libtmux.pytest_plugin.control_mode in the
Factories section so {fixture}`control_mode` resolves to a
stable anchor.
- Verified: ruff/mypy/pytest clean; just build-docs succeeds with
no new warnings; rendered HTML confirms every role resolves to
a clickable internal link.
why: 0.56 added quiet= and values_only= to the private _show_options_raw() but never threaded them through the three layers above (_show_options_dict, _show_options, show_options), so the public method silently lacked the kwargs. Calling Server().show_options(quiet=True) raised TypeError: got an unexpected keyword argument 'quiet'. what: - _show_options_dict, _show_options, show_options: add quiet: bool | None = None to each signature and forward it down the call chain to _show_options_raw. - show_options: document the new kwarg with versionadded:: 0.56. - tests/test_options.py: add test_show_options_quiet_public to exercise the public path and prevent this class of "private kwarg not threaded through" regression. - values_only= is intentionally NOT exposed on the public method: it changes tmux output to bare values, which is incompatible with show_options' dict return contract. Future work if needed.
why: 0.56 added values_only= to the private _show_options_raw() but it was never exposed on the public show_options() — and cannot be without changing the dict return contract (values_only=True emits bare values, not name/value pairs the parser expects). The CHANGES claim was aspirational; correct it to match shipped behavior. quiet= remains and is now actually wired through (see preceding commit). what: - CHANGES (0.56.x): show_options entry now lists only `quiet=` as a new kwarg.
Summary
This PR expands libtmux's tmux command parity across
Server,Session,Window, andPane, with a focus on interactive and client-dependent commands. It also tightens several flag mappings and state-refresh behaviors so the high-level API matches tmux semantics more closely.In addition to the new command coverage, this includes the three follow-up fixes from review:
Session.detach_client()no longer forces-s, so targeted detach behaves like tmux.Window.move_window()now refreshes after successful moves, so returned objects are not stale after relative or cross-session moves.ControlModenow bindsclient_nameto the spawned client by matchingclient_pid, which fixes multi-client races.Main Changes
New or expanded tmux command coverage
Serversupport for commands such asbind_key,unbind_key,list_keys,list_commands,start_server,lock_server,lock_client,refresh_client,suspend_client,server_access,run_shell,if_shell,source_file, buffer commands,confirm_before,command_prompt, anddisplay_menu.Session.detach_client()and window navigation helpers.Windowsupport formove_windowflags,select_layoutflags,last_pane,next_layout,previous_layout,rotate,swap,respawn,link, andunlink.Panecoverage fordisplay_popup,capture_pane,send_keys,select,copy_mode,clock_mode,choose_*,customize_mode,display_panes,find_window,paste_buffer,clear_history,pipe,join,break_pane,move,respawn, and related flags.tmux semantics and compatibility fixes
move-window -ras standalone renumbering.Test and tooling support
ControlModeand pytest fixture support for commands that require a real attached client..claude/andskills/tmux-parity/to help maintain command coverage against tmux.Testing
Ran:
uv run ruff check . --fix --show-fixesuv run ruff format .uv run mypyuv run py.test --reruns 0 -vvvLatest full run:
1067 passed, 1 skippedNotes
origin/masteris broad because this branch includes both the parity feature work and the review-driven correctness fixes on top.